Microproyecto 1: Generación de paleta de colores a partir de imágenes con técnias de Machine Learning No supervisado
¶

Equipo 16: Mateo Hernandez Gualdron - Martin Alejandro Quintero Rincon¶

Universidad de los Andes, Maestría en Inteligencia Artificial.¶

Machine Learning No Supervisado (MLNS)¶


En este notebook se analiza la paleta de colores en el arte mediante técnicas de aprendizaje automático no supervisado.
La paleta de colores, entendida como el conjunto característico de tonalidades empleadas por un artista o movimiento, trasciende lo meramente estético: refleja decisiones técnicas, condicionantes históricos y dimensiones psicológicas y simbólicas.

El estudio de las paletas permite comprender cómo los artistas expresan emociones y construyen su identidad visual, además de evidenciar la influencia de factores como la disponibilidad de pigmentos, las corrientes culturales y las narrativas iconográficas. Asimismo, ofrece claves para reconocer la armonía y coherencia estilística propias de cada periodo artístico.


Obras seleccionadas para el análisis (10)¶

Obra Año Descripción breve Periodo
Portrait of Lorenzo the Magnificent, Agnolo Bronzino ca. 1530 Retrato manierista de Lorenzo de Médici, destacando la elegancia estilizada. Manierismo / Renacimiento tardío
The Incredulity of Saint Thomas, Caravaggio 1601–1602 Escena bíblica con realismo dramático y claroscuro intenso. Barroco
The Wanderer above the Sea of Fog, Caspar David Friedrich 1818 Figura solitaria frente al sublime paisaje nebuloso, símbolo del Romanticismo. Romanticismo
Christ among the Doctors, Albrecht Dürer 1506 Cristo niño discutiendo con los doctores, composición detallada del Renacimiento. Renacimiento del Norte
El Matador, Pablo Picasso 1970 Figura taurina estilizada, síntesis tardía de su lenguaje cubista. Cubismo tardío
Western Motel, Edward Hopper 1957 Escena silenciosa y enigmática en un motel norteamericano. Realismo / New Realism
Charing Cross Bridge, André Derain 1906 Paisaje urbano con pinceladas puntillistas y colores vibrantes. Puntillismo
Still Life, Juan Gris ca. 1910–1914 Bodegón analítico con fragmentación geométrica de objetos. Cubismo analítico
Goya’s Lover, Walasse Ting 1977 Expresividad gestual y color vibrante en estilo libre. Action Painting
Vesuvius, Andy Warhol 1985 Repetición icónica del volcán Vesubio con estética pop. Pop Art
In [11]:
! pip install opencv-python
! pip install umap-learn
Requirement already satisfied: opencv-python in /usr/local/lib/python3.12/dist-packages (4.12.0.88)
Requirement already satisfied: numpy<2.3.0,>=2 in /usr/local/lib/python3.12/dist-packages (from opencv-python) (2.0.2)
Requirement already satisfied: umap-learn in /usr/local/lib/python3.12/dist-packages (0.5.9.post2)
Requirement already satisfied: numpy>=1.23 in /usr/local/lib/python3.12/dist-packages (from umap-learn) (2.0.2)
Requirement already satisfied: scipy>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from umap-learn) (1.16.1)
Requirement already satisfied: scikit-learn>=1.6 in /usr/local/lib/python3.12/dist-packages (from umap-learn) (1.6.1)
Requirement already satisfied: numba>=0.51.2 in /usr/local/lib/python3.12/dist-packages (from umap-learn) (0.60.0)
Requirement already satisfied: pynndescent>=0.5 in /usr/local/lib/python3.12/dist-packages (from umap-learn) (0.5.13)
Requirement already satisfied: tqdm in /usr/local/lib/python3.12/dist-packages (from umap-learn) (4.67.1)
Requirement already satisfied: llvmlite<0.44,>=0.43.0dev0 in /usr/local/lib/python3.12/dist-packages (from numba>=0.51.2->umap-learn) (0.43.0)
Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.12/dist-packages (from pynndescent>=0.5->umap-learn) (1.5.2)
Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.12/dist-packages (from scikit-learn>=1.6->umap-learn) (3.6.0)
In [7]:
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np
import cv2
import os
import pandas as pd
In [8]:
#Primero: cargar el Drive:
from google.colab import drive
drive.mount('/content/Mdrive')
%cd '/content/Mdrive/MyDrive/Andes/202521/NoSupervisado/'
! pwd
! dir
Drive already mounted at /content/Mdrive; to attempt to forcibly remount, call drive.mount("/content/Mdrive", force_remount=True).
/content/Mdrive/MyDrive/Andes/202521/NoSupervisado
/content/Mdrive/MyDrive/Andes/202521/NoSupervisado
Customer_Data.csv    Dataset		      Microproyecto1.ipynb
Customer_data.ipynb  ImageSegmentation.ipynb
In [24]:
def leerfoto(foto, clusters):
    imagen = cv2.imread(foto)
    new_width = 200
    new_height = 200
    resized_image = cv2.resize(imagen, (new_width, new_height))
    # BGR2RGB
    imagen_rgb = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB)
    h, w, c = imagen_rgb.shape
    datos_clustering = imagen_rgb.reshape((-1, 3))
    kmeans = KMeans(n_clusters=clusters, random_state=77, n_init="auto")
    kmeans.fit(datos_clustering)
    etiquetas = kmeans.labels_
    centros = kmeans.cluster_centers_
    colores = pd.Series(etiquetas).value_counts()
    datos_clustering_df = pd.DataFrame(centros,columns=['R','G','B'])
    datos_clustering_df['counts'] = colores.values
    #display(datos_clustering_df)
    # Recreate the segmented image
    imagen_segmentada = centros[etiquetas]
    # Reshape
    imagen_segmentada = imagen_segmentada.reshape((h, w, c))
    imagen_segmentada = imagen_segmentada.astype(np.uint8)
    centroides_img = np.zeros((100, len(centros) * 35, 3), dtype=np.uint8)
    for i, centro in enumerate(centros):
        inicio = i * 35
        fin = (i + 1) * 35
        centroides_img[:, inicio:fin] = centro.astype(np.uint8)

    # Calcular centroides
    posiciones_centroides = []
    for i in range(len(centros)):
        mask_cluster = (etiquetas == i)

        # Coordenadas
        indices_cluster = np.where(mask_cluster)[0]

        if len(indices_cluster) > 0:
            # fila, columna
            coords_2d = np.unravel_index(indices_cluster, (h, w))
            filas = coords_2d[0]  # y
            columnas = coords_2d[1]  # x

            # Mean cluster
            centroide_x = np.mean(columnas)
            centroide_y = np.mean(filas)
            posiciones_centroides.append((centroide_x, centroide_y))

    posiciones_centroides = np.array(posiciones_centroides)

    # TSNE
    datos_clustering_embedded = TSNE(n_components=2, learning_rate='auto',
                    init='random', perplexity=30).fit_transform(datos_clustering)

    datos_clustering_embedded.shape

    # Plot
    f, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(1, 5, figsize=(25, 5))
    ax1.imshow(imagen_rgb)
    ax1.set_title(f'{foto}')
    ax1.axis('off')


    ax2.imshow(imagen_segmentada)
    if len(posiciones_centroides) > 0:
          ax2.scatter(posiciones_centroides[:, 0], posiciones_centroides[:, 1],
                     c='red', s=100, marker='x', linewidths=3)
    ax2.set_title(f'Segmentada K-Means (k={kmeans.n_clusters})')
    ax2.axis('off')


    ax3.imshow(centroides_img)
    ax3.set_title('Paleta de colores')
    ax3.axis('off')

    ax4.bar(range(len(centros)), datos_clustering_df['counts'], color=centros/255, width=0.5)
    ax4.set_xticks(range(len(centros)))
    ax4.set_xticklabels([f'C{i}' for i in range(len(centros))])
    for i in range(len(centros)):
        # Posición dinámica del texto basada en la altura de cada barra
        altura_barra = datos_clustering_df['counts'][i]
        ax4.text(x=i, y=250,
                 s=f'RGB({datos_clustering_df["R"][i]:.0f}, {datos_clustering_df["G"][i]:.0f}, {datos_clustering_df["B"][i]:.0f})',
                 bbox=dict(facecolor='yellow', edgecolor='black', boxstyle='round,pad=0.1', alpha=0.4),
                 rotation=90, ha='center', va='bottom')
    ax4.set_title('Histogram of Color Frequency')

    colores_clusters = centros[etiquetas] / 255.0
    ax5.scatter(datos_clustering_embedded[:,0], datos_clustering_embedded[:,1],
               c=colores_clusters, s=1)
    ax5.set_title('t-SNE con colores reales')

    plt.tight_layout()
    plt.show()

    return np.shape(imagen_rgb),
In [25]:
path = 'Dataset/'
for i in os.listdir(path):
    leerfoto(os.path.join(path, i), 7)
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Conclusiones¶

Microproyecto 1: Generación de Paletas de Colores mediante K-Means¶

El Microproyecto 1 del curso Machine Learning No Supervisado desarrolló un método automatizado implementado en la función leerfoto(path, clusters) para extraer paletas cromáticas representativas de obras de arte mediante técnicas de clustering. El sistema integra preprocesamiento de imagen, algoritmo K-Means para agrupación y visualización t-SNE.

Se analizaron 10 obras del dataset de Kaggle, seleccionadas estratégicamente para representar la diversidad estilística: arte clásico (Caravaggio), pop art (Warhol), cubismo (Juan Gris), y movimientos vanguardistas, asegurando variabilidad en las distribuciones cromáticas.


Pipeline de Procesamiento¶

El pipeline ejecuta las siguientes etapas:

Redimensionamiento optimizado: Reducción a 200×200 píxeles tras análisis empírico. Las pruebas con 50×50 píxeles convergían rápidamente pero sacrificaban calidad visual, mientras que 500×500 píxeles requerían 47 minutos de procesamiento en Google Colab gratuito, siendo computacionalmente inviable.

Normalización cromática: Conversión del espacio BGR (OpenCV) al estándar RGB para correcta interpretación de colores.

Reestructuración matricial: Transformación de la imagen tridimensional (altura × anchura × 3 canales) a matriz bidimensional (n_píxeles × 3) para procesamiento algorítmico.

Clustering y generación de paleta: Aplicación de K-Means con extracción de 7 centroides cromáticos representativos.


Fundamentación del Algoritmo K-Means¶

La selección de K-Means sobre alternativas como K-Medoids responde a consideraciones teóricas y prácticas específicas del dominio cromático:

Ventaja conceptual: K-Means genera centroides como promedios ponderados de los píxeles asignados a cada cluster, creando colores sintéticos que representan matemáticamente la distribución cromática real. Esta característica es fundamental para paletas de diseño, donde se requieren tonos que capturen la esencia cromática global, no necesariamente píxeles existentes en la imagen original. Se podría pensar típicamente como el estilo de cada autor.

Superioridad sobre K-Medoids para una paleta de estilo: Mientras K-Medoids seleccionaría píxeles específicos existentes como representantes de cada cluster, K-Means calcula el color óptimo teórico para cada agrupación. En el contexto artístico, esto significa que cada color de la paleta representa la "media armónica" de una región cromática, proporcionando mayor coherencia estética y utilidad práctica para aplicaciones de diseño.

Robustez estadística: K-Medioides es más robusto a valores atípicos que K-Means, porque seleccionaría un punto real de pixeles; sin embargo, en una muestra artística el ruido, en general, va a ser bajo y los valores atípicos no son un problema real. Es difícil pensar en un "outlier" dentro de una pintura. Todavía si hubiera un solo pixel anómalo no haría una gran diferencia con respecto al centroide.

La configuración de k=7 clusters equilibra granularidad cromática con usabilidad práctica en diseño gráfico.

En cualquier caso, si el enunciado o la situación requiriera explícitamente que la paleta de colores estuviera compuesta únicamente por colores presentes de forma totalmente fiel en la obra, sin generar representaciones promedio o sintéticas, podría aplicarse K-Medoids. Este algoritmo selecciona como representantes píxeles reales de la imagen, garantizando que cada color de la paleta corresponda a un valor cromático efectivamente existente en la obra original. Esto podría ser más útil para restauración o análisis técnico de pigmentos, no tanto para el análisis estilístico y diseño.


Análisis Crítico del Parámetro Perplexity en t-SNE¶

La experimentación exhaustiva reveló que el parámetro perplexity constituye el factor determinante en la interpretabilidad de las visualizaciones bidimensionales:

Perplexity bajo (3): Genera estructuras colapsadas donde todos los puntos se superponen en una nube densa, eliminando cualquier separación interpretable entre clusters cromáticos. Esta configuración prioriza relaciones de vecindad extremadamente locales, perdiendo la estructura global.

Perplexity optimizado (35): Produce separaciones claras entre clusters, manteniendo proximidades coherentes con las relaciones cromáticas reales. Los grupos se organizan espacialmente según similitud cromática, facilitando la interpretación visual.


Validación mediante Análisis Estilístico Diferencial¶

El método demostró sensibilidad estilística distintiva:

Andy Warhol (Vesubio): Las obras pop art generan clusters perfectamente separados en el espacio t-SNE, reflejando las transiciones cromáticas abruptas características del estilo. La ausencia de gradientes produce separaciones nítidas entre regiones de color puro.

Caravaggio (La Incredulidad de Santo Tomás): Las obras clásicas exhiben distribuciones en degradé continuo, donde los clusters forman transiciones suaves que reflejan el característico claroscuro. Los centroides se organizan en gradientes que respetan las transiciones tonales naturales de la técnica pictórica.


Conclusión Final¶

Esta diferenciación algorítmica valida la capacidad del sistema para capturar y representar matemáticamente las características estilísticas intrínsecas de diferentes movimientos artísticos, confirmando la robustez metodológica del enfoque propuesto.